﻿using UnityEngine;
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using Common.Cryptography;

[RequireComponent (typeof (SaveInfo))]
public class SaveGameManager : Singleton<SaveGameManager> 
{
	public string m_FileName;
	public string m_FileExtension;
	public string m_GamePassword;

	public string m_TestStringSave;

	public TextMesh m_SaveTest;

	private string m_DecryptedData;

	public SaveData m_TEst;
	
	public SaveData m_TEst2;

	public void TEST(SaveData save)
	{
		m_TEst2 = save;
	}

	[HideInInspector]
	public static string FilePath;

	[System.Serializable]
	public class SaveData
	{
		public object m_Value;
		
		public SaveData()
		{
			m_Value = null;
		}
		
		public SaveData(object i_Value)
		{
			m_Value = i_Value;
		}
		/*
		public Dictionary<SaveDataID.AttributeID, object> m_Info;
		
		public SaveData()
		{
			m_Info = new Dictionary<SaveDataID.AttributeID, object>();
		}

		public SaveData(object i_Data)
		{
			m_Info = new Dictionary<SaveDataID.AttributeID, object>();
		}
		*/

		/*
		public void SetData(SaveDataID.AttributeID i_Key, object i_Object)
		{
			if(Application.isWebPlayer)
			{
				if(i_Object.GetType() == typeof(int))
				{
					PlayerPrefs.SetInt(i_Key.ToString(), (int)i_Object);
				}
				else if(i_Object.GetType() == typeof(float))
				{
					PlayerPrefs.SetFloat(i_Key.ToString(), (float)i_Object);
				}
				else if(i_Object.GetType() == typeof(string))
				{
					PlayerPrefs.SetString(i_Key.ToString(), (string)i_Object);
				}
			}
			else
			{
				if(m_Info.ContainsKey(i_Key))
				{
					m_Info[i_Key] = i_Object;
				}
				else
				{
					m_Info.Add(i_Key, i_Object);
				}
			}
		}
		
		public object GetValue(SaveDataID.AttributeID i_Key, Type i_Type)
		{
			if(Application.isWebPlayer)
			{
				if(i_Type == typeof(int))
				{
					return (object)PlayerPrefs.GetInt(i_Key.ToString());
				}
				else if(i_Type == typeof(float))
				{
					return (object)PlayerPrefs.GetFloat(i_Key.ToString());
				}
				else if(i_Type == typeof(string))
				{
					return (object)PlayerPrefs.GetString(i_Key.ToString());
				}
				return null;
			}
			else
			{
				if(m_Info.ContainsKey(i_Key))
				{
					return m_Info[i_Key];
				}
				else
				{
					if(i_Type == typeof(int))
					{
						return 0;
					}
					else if(i_Type == typeof(float))
					{
						return 0f;
					}
					else if(i_Type == typeof(string))
					{
						return "";
					}
				}
				return null;
			}
		}
		*/
	}

	/*
	private SaveData m_Data;
	public SaveData Data
	{
		get { return m_Data; }
	}
	*/

	#region Private Methods

	// Is called by SaveInfo
	public void Initialize()
	{
		if(!Application.isWebPlayer)
		{
			FilePath = Application.persistentDataPath + "/" + m_FileName + "." + m_FileExtension;
			Load(true);
			SetupData(m_DecryptedData);
			//m_Data = new SaveData(Load (true));
			//SetDictionary(m_DecryptedData);
			if(!File.Exists(FilePath))
			{
				Save ();
			}
		}
		else
		{
			//m_Data = new SaveData();
		}
	}

	private void Awake()
	{
		base.Awake();
	}

	private void Start()
	{
		//SaveGameManager.Instance.SetData("Test1", 18);
		//SaveGameManager.Instance.SetData("Test2", 76.55f);
		//SaveGameManager.Instance.SetData("Test3", "STRING");

		//SetData("TestString", m_TestStringSave);

		//int test1 = (int)SaveGameManager.Instance.GetValue(SaveDataID.AttributeID.INT, SaveInfo.SaveDataType.INT);
		//float test2 = (float)SaveGameManager.Instance.GetValue(SaveDataID.AttributeID.FLOAT, SaveInfo.SaveDataType.FLOAT);
		string test3 = (string)SaveGameManager.Instance.GetValue(SaveDataID.AttributeID.STRING, SaveInfo.SaveDataType.STRING);

		if(m_SaveTest) m_SaveTest.text = (string)SaveGameManager.Instance.GetValue(SaveDataID.AttributeID.STRING, SaveInfo.SaveDataType.STRING);
	}

	private void SetDictionary(string i_StringData)
	{
		if(i_StringData == "" || i_StringData == null)
		{
			return;
		}

		char[] charDataLineSplits = new char[]
		{
			';'
		};

		string[] splitData = i_StringData.Split(charDataLineSplits, StringSplitOptions.RemoveEmptyEntries);

		foreach(string s in splitData)
		{
			string type = s.Split(':')[0].Split('-')[0];
			string key = s.Split(':')[0].Split('-')[1];
			string value = s.Split(':')[1];
			
			if(type == typeof(string).FullName)
			{
				//m_Data.m_Info.Add(key, value);
			}
			else if(type == typeof(int).FullName)
			{
				//m_Data.m_Info.Add(key, int.Parse(value));
			}
			else if(type == typeof(float).FullName)
			{
				//m_Data.m_Info.Add(key, float.Parse(value));
			}
			else if(type == typeof(object).FullName)
			{
			}
		}
	}

	private void SetupData(string i_StringData)
	{
		if(string.IsNullOrEmpty(i_StringData))
		{
			return;
		}
		
		char[] charDataLineSplits = new char[]
		{
			';'
		};
		
		string[] splitData = i_StringData.Split(charDataLineSplits, StringSplitOptions.RemoveEmptyEntries);
		int count = 0;
		
		foreach(string s in splitData)
		{
			string type = s.Split(':')[0].Split('-')[0];
			string key = s.Split(':')[0].Split('-')[1];
			string value = s.Split(':')[1];
			
			if(type == SaveInfo.SaveDataType.STRING.ToString())
			{
				//m_Data.m_Info.Add(key, value);
				SaveInfo.Instance.m_AllSaveInfo[count].m_SaveData.m_Value = value;
			}
			else if(type == SaveInfo.SaveDataType.INT.ToString())
			{
				//m_Data.m_Info.Add(key, int.Parse(value));
				SaveInfo.Instance.m_AllSaveInfo[count].m_SaveData.m_Value = int.Parse(value);
			}
			else if(type == SaveInfo.SaveDataType.FLOAT.ToString())
			{
				//m_Data.m_Info.Add(key, float.Parse(value));
				SaveInfo.Instance.m_AllSaveInfo[count].m_SaveData.m_Value = float.Parse(value);
			}
			else if(type == typeof(object).FullName)
			{
			}

			count++;
		}
	}
	#endregion

	#region Public Methods
	
	public void Save() 
	{
		try {
			Debug.Log("Writing Stream to Disk.");
			//Stream fileStream = File.Open(i_Filename, FileMode.Create);
			
			string encryptedData = AES.Encrypt(SLSerializer.ConvertToString(SaveInfo.Instance.m_AllSaveInfo), m_GamePassword);
			
			FileStream fileStream = File.Open(FilePath, FileMode.Create);
			BinaryFormatter formatter = new BinaryFormatter();
			//formatter.Serialize(fileStream, (object)m_Data);
			formatter.Serialize(fileStream, (object)encryptedData);
			fileStream.Close();
		} catch(Exception e) {
			Debug.LogWarning("SaveGameManager.(): Failed to serialize object to a file " + FilePath + " (Reason: " + e.ToString() + ")");
		}
	}

	// TODO
	public void SaveAll() 
	{
		for(int i = 0; i < SaveInfo.Instance.m_AllSaveInfo.Length; i++)
		{
			SetData(SaveInfo.Instance.GetAttributeID(SaveInfo.Instance.m_AllSaveInfo[i].m_AttributeName), SaveInfo.Instance.m_AllSaveInfo[i].m_SaveData, false);
			//object test = SaveInfo.Instance.m_AllSaveInfo[i].m_AttributeValue;
		}
		//Dictionary<string, SaveDataObject> allSaveDatas = new Dictionary<string, SaveDataObject>(m_Data.m_AllSaveDatas);
		//foreach(KeyValuePair<string, SaveDataObject> kv in allSaveDatas)
		//{
		//	SetData(kv.Key, kv.Value.m_ObjectToSave);
		//}
		Save ();
	}
	
	public object Load(bool i_FirstLoad = false) 
	{
		try {
			Debug.Log("Reading Stream from Disk.");

#if UNITY_WEBPLAYER
			Stream fileStream = File.Open(FilePath, FileMode.Open);
#else
			Stream fileStream = File.Open(FilePath, FileMode.Open, FileAccess.Read);
#endif


			BinaryFormatter formatter = new BinaryFormatter();
			object obj = formatter.Deserialize(fileStream);

			m_DecryptedData = AES.Decrypt(obj as string, m_GamePassword);

			fileStream.Close();

			if(m_DecryptedData == "")
				obj = null;

			return obj;
		} catch(Exception e) {
			if(!i_FirstLoad)
				Debug.LogWarning("SaveLoad.Load(): Failed to deserialize a file " + FilePath + " (Reason: " + e.ToString() + ")");
			return null;
		}       
	}

	public void SetData(SaveDataID.AttributeID i_Key, SaveGameManager.SaveData i_SaveData, bool i_AutoSave = true)
	{
		//m_Data.SetData(i_Key, i_Object);
		
		if(Application.isWebPlayer)
		{
			if(i_SaveData.m_Value.GetType() == typeof(int))
			{
				PlayerPrefs.SetInt(i_Key.ToString(), (int)i_SaveData.m_Value);
			}
			else if(i_SaveData.m_Value.GetType() == typeof(float))
			{
				PlayerPrefs.SetFloat(i_Key.ToString(), (float)i_SaveData.m_Value);
			}
			else if(i_SaveData.m_Value.GetType() == typeof(string))
			{
				PlayerPrefs.SetString(i_Key.ToString(), (string)i_SaveData.m_Value);
			}
		}
		else
		{
			for(int i = 0; i < SaveInfo.Instance.m_AllSaveInfo.Length; i++)
			{
				if(SaveInfo.Instance.m_AllSaveInfo[i].m_AttributeName == i_Key.ToString())
				{
					SaveInfo.Instance.m_AllSaveInfo[i].m_SaveData = i_SaveData;
					break;
				}
			}

			/*
			if(m_Info.ContainsKey(i_Key))
			{
				m_Info[i_Key] = i_Object;
			}
			else
			{
				m_Info.Add(i_Key, i_Object);
			}
			*/
		}

		if(!Application.isWebPlayer)
		{
			if(i_AutoSave)
			{
				Save ();
			}
		}
	}

	public object GetValue(SaveDataID.AttributeID i_Key, SaveInfo.SaveDataType i_Type)	
	{
		if(Application.isWebPlayer)
		{
			if(i_Type == SaveInfo.SaveDataType.INT)
			{
				return (object)PlayerPrefs.GetInt(i_Key.ToString());
			}
			else if(i_Type == SaveInfo.SaveDataType.FLOAT)
			{
				return (object)PlayerPrefs.GetFloat(i_Key.ToString());
			}
			else if(i_Type == SaveInfo.SaveDataType.STRING)
			{
				return (object)PlayerPrefs.GetString(i_Key.ToString());
			}
			return null;
		}
		else
		{
			for(int i = 0; i < SaveInfo.Instance.m_AllSaveInfo.Length; i++)
			{
				if(SaveInfo.Instance.m_AllSaveInfo[i].m_AttributeName == i_Key.ToString())
				{
					return SaveInfo.Instance.m_AllSaveInfo[i].m_SaveData.m_Value;
				}
			}

			// If Key Not Found
			if(i_Type == SaveInfo.SaveDataType.INT)
			{
				return 0;
			}
			else if(i_Type ==  SaveInfo.SaveDataType.FLOAT)
			{
				return 0f;
			}
			else if(i_Type ==  SaveInfo.SaveDataType.STRING)
			{
				return "";
			}
			/*
			if(m_Info.ContainsKey(i_Key))
			{
				return m_Info[i_Key];
			}
			else
			{
				if(i_Type == typeof(int))
				{
					return 0;
				}
				else if(i_Type == typeof(float))
				{
					return 0f;
				}
				else if(i_Type == typeof(string))
				{
					return "";
				}
			}
			*/
			return null;
		}

		//return m_Data.GetValue(i_Key, i_Type);
	}

	public override void OnDestroy()
	{
		SaveAll();
		base.OnDestroy();
	}
	#endregion
}